home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.002 / stk-3 / STk-3.1 / Tk / generic / tkCanvLine.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-31  |  47.9 KB  |  1,636 lines

  1. /* 
  2.  * tkCanvLine.c --
  3.  *
  4.  *    This file implements line items for canvas widgets.
  5.  *
  6.  * Copyright (c) 1991-1994 The Regents of the University of California.
  7.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * SCCS: @(#) tkCanvLine.c 1.43 96/02/15 18:52:30
  13.  */
  14.  
  15. #include <stdio.h>
  16. #include "tkInt.h"
  17. #include "tkPort.h"
  18.  
  19. /*
  20.  * The structure below defines the record for each line item.
  21.  */
  22.  
  23. typedef struct LineItem  {
  24.     Tk_Item header;        /* Generic stuff that's the same for all
  25.                  * types.  MUST BE FIRST IN STRUCTURE. */
  26.     Tk_Canvas canvas;        /* Canvas containing item.  Needed for
  27.                  * parsing arrow shapes. */
  28.     int numPoints;        /* Number of points in line (always >= 2). */
  29.     double *coordPtr;        /* Pointer to malloc-ed array containing
  30.                  * x- and y-coords of all points in line.
  31.                  * X-coords are even-valued indices, y-coords
  32.                  * are corresponding odd-valued indices. If
  33.                  * the line has arrowheads then the first
  34.                  * and last points have been adjusted to refer
  35.                  * to the necks of the arrowheads rather than
  36.                  * their tips.  The actual endpoints are
  37.                  * stored in the *firstArrowPtr and
  38.                  * *lastArrowPtr, if they exist. */
  39.     int width;            /* Width of line. */
  40.     XColor *fg;            /* Foreground color for line. */
  41.     Pixmap fillStipple;        /* Stipple bitmap for filling line. */
  42.     int capStyle;        /* Cap style for line. */
  43.     int joinStyle;        /* Join style for line. */
  44.     GC gc;            /* Graphics context for filling line. */
  45.     GC arrowGC;            /* Graphics context for drawing arrowheads. */
  46.     Tk_Uid arrow;        /* Indicates whether or not to draw arrowheads:
  47.                  * "none", "first", "last", or "both". */
  48.     float arrowShapeA;        /* Distance from tip of arrowhead to center. */
  49.     float arrowShapeB;        /* Distance from tip of arrowhead to trailing
  50.                  * point, measured along shaft. */
  51.     float arrowShapeC;        /* Distance of trailing points from outside
  52.                  * edge of shaft. */
  53.     double *firstArrowPtr;    /* Points to array of PTS_IN_ARROW points
  54.                  * describing polygon for arrowhead at first
  55.                  * point in line.  First point of arrowhead
  56.                  * is tip.  Malloc'ed.  NULL means no arrowhead
  57.                  * at first point. */
  58.     double *lastArrowPtr;    /* Points to polygon for arrowhead at last
  59.                  * point in line (PTS_IN_ARROW points, first
  60.                  * of which is tip).  Malloc'ed.  NULL means
  61.                  * no arrowhead at last point. */
  62.     int smooth;            /* Non-zero means draw line smoothed (i.e.
  63.                  * with Bezier splines). */
  64.     int splineSteps;        /* Number of steps in each spline segment. */
  65. } LineItem;
  66.  
  67. /*
  68.  * Number of points in an arrowHead:
  69.  */
  70.  
  71. #define PTS_IN_ARROW 6
  72.  
  73. /*
  74.  * Prototypes for procedures defined in this file:
  75.  */
  76.  
  77. static int        ArrowheadPostscript _ANSI_ARGS_((Tcl_Interp *interp,
  78.                 Tk_Canvas canvas, LineItem *linePtr,
  79.                 double *arrowPtr));
  80. static void        ComputeLineBbox _ANSI_ARGS_((Tk_Canvas canvas,
  81.                 LineItem *linePtr));
  82. static int        ConfigureLine _ANSI_ARGS_((Tcl_Interp *interp,
  83.                 Tk_Canvas canvas, Tk_Item *itemPtr, int argc,
  84.                 char **argv, int flags));
  85. static int        ConfigureArrows _ANSI_ARGS_((Tk_Canvas canvas,
  86.                 LineItem *linePtr));
  87. static int        CreateLine _ANSI_ARGS_((Tcl_Interp *interp,
  88.                 Tk_Canvas canvas, struct Tk_Item *itemPtr,
  89.                 int argc, char **argv));
  90. static void        DeleteLine _ANSI_ARGS_((Tk_Canvas canvas,
  91.                 Tk_Item *itemPtr, Display *display));
  92. static void        DisplayLine _ANSI_ARGS_((Tk_Canvas canvas,
  93.                 Tk_Item *itemPtr, Display *display, Drawable dst,
  94.                 int x, int y, int width, int height));
  95. static int        LineCoords _ANSI_ARGS_((Tcl_Interp *interp,
  96.                 Tk_Canvas canvas, Tk_Item *itemPtr,
  97.                 int argc, char **argv));
  98. static int        LineToArea _ANSI_ARGS_((Tk_Canvas canvas,
  99.                 Tk_Item *itemPtr, double *rectPtr));
  100. static double        LineToPoint _ANSI_ARGS_((Tk_Canvas canvas,
  101.                 Tk_Item *itemPtr, double *coordPtr));
  102. static int        LineToPostscript _ANSI_ARGS_((Tcl_Interp *interp,
  103.                 Tk_Canvas canvas, Tk_Item *itemPtr, int prepass));
  104. static int        ParseArrowShape _ANSI_ARGS_((ClientData clientData,
  105.                 Tcl_Interp *interp, Tk_Window tkwin, char *value,
  106.                 char *recordPtr, int offset));
  107. static char *        PrintArrowShape _ANSI_ARGS_((ClientData clientData,
  108.                 Tk_Window tkwin, char *recordPtr, int offset,
  109.                 Tcl_FreeProc **freeProcPtr));
  110. static void        ScaleLine _ANSI_ARGS_((Tk_Canvas canvas,
  111.                 Tk_Item *itemPtr, double originX, double originY,
  112.                 double scaleX, double scaleY));
  113. static void        TranslateLine _ANSI_ARGS_((Tk_Canvas canvas,
  114.                 Tk_Item *itemPtr, double deltaX, double deltaY));
  115.  
  116. /*
  117.  * Information used for parsing configuration specs.  If you change any
  118.  * of the default strings, be sure to change the corresponding default
  119.  * values in CreateLine.
  120.  */
  121.  
  122. static Tk_CustomOption arrowShapeOption = {ParseArrowShape,
  123.     PrintArrowShape, (ClientData) NULL};
  124. static Tk_CustomOption tagsOption = {Tk_CanvasTagsParseProc,
  125.     Tk_CanvasTagsPrintProc, (ClientData) NULL
  126. };
  127.  
  128. static Tk_ConfigSpec configSpecs[] = {
  129.     {TK_CONFIG_UID, "-arrow", (char *) NULL, (char *) NULL,
  130.     "none", Tk_Offset(LineItem, arrow), TK_CONFIG_DONT_SET_DEFAULT},
  131.     {TK_CONFIG_CUSTOM, "-arrowshape", (char *) NULL, (char *) NULL,
  132. #ifdef STk_CODE
  133.     "(8 10 3)", Tk_Offset(LineItem, arrowShapeA),
  134. #else
  135.     "8 10 3", Tk_Offset(LineItem, arrowShapeA),
  136. #endif
  137.     TK_CONFIG_DONT_SET_DEFAULT, &arrowShapeOption},
  138.     {TK_CONFIG_CAP_STYLE, "-capstyle", (char *) NULL, (char *) NULL,
  139.     "butt", Tk_Offset(LineItem, capStyle), TK_CONFIG_DONT_SET_DEFAULT},
  140.     {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL,
  141.     "black", Tk_Offset(LineItem, fg), TK_CONFIG_NULL_OK},
  142.     {TK_CONFIG_JOIN_STYLE, "-joinstyle", (char *) NULL, (char *) NULL,
  143.     "round", Tk_Offset(LineItem, joinStyle), TK_CONFIG_DONT_SET_DEFAULT},
  144.     {TK_CONFIG_BOOLEAN, "-smooth", (char *) NULL, (char *) NULL,
  145. #ifdef STk_CODE
  146.     "#f", Tk_Offset(LineItem, smooth), TK_CONFIG_DONT_SET_DEFAULT},
  147. #else
  148.     "0", Tk_Offset(LineItem, smooth), TK_CONFIG_DONT_SET_DEFAULT},
  149. #endif
  150.     {TK_CONFIG_INT, "-splinesteps", (char *) NULL, (char *) NULL,
  151.     "12", Tk_Offset(LineItem, splineSteps), TK_CONFIG_DONT_SET_DEFAULT},
  152.     {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL,
  153.     (char *) NULL, Tk_Offset(LineItem, fillStipple), TK_CONFIG_NULL_OK},
  154.     {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
  155.     (char *) NULL, 0, TK_CONFIG_NULL_OK, &tagsOption},
  156.     {TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL,
  157.     "1", Tk_Offset(LineItem, width), TK_CONFIG_DONT_SET_DEFAULT},
  158.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  159.     (char *) NULL, 0, 0}
  160. };
  161.  
  162. /*
  163.  * The structures below defines the line item type by means
  164.  * of procedures that can be invoked by generic item code.
  165.  */
  166.  
  167. Tk_ItemType tkLineType = {
  168.     "line",                /* name */
  169.     sizeof(LineItem),            /* itemSize */
  170.     CreateLine,                /* createProc */
  171.     configSpecs,            /* configSpecs */
  172.     ConfigureLine,            /* configureProc */
  173.     LineCoords,                /* coordProc */
  174.     DeleteLine,                /* deleteProc */
  175.     DisplayLine,            /* displayProc */
  176.     0,                    /* alwaysRedraw */
  177.     LineToPoint,            /* pointProc */
  178.     LineToArea,                /* areaProc */
  179.     LineToPostscript,            /* postscriptProc */
  180.     ScaleLine,                /* scaleProc */
  181.     TranslateLine,            /* translateProc */
  182.     (Tk_ItemIndexProc *) NULL,        /* indexProc */
  183.     (Tk_ItemCursorProc *) NULL,        /* icursorProc */
  184.     (Tk_ItemSelectionProc *) NULL,    /* selectionProc */
  185.     (Tk_ItemInsertProc *) NULL,        /* insertProc */
  186.     (Tk_ItemDCharsProc *) NULL,        /* dTextProc */
  187.     (Tk_ItemType *) NULL        /* nextPtr */
  188. };
  189.  
  190. /*
  191.  * The Tk_Uid's below refer to uids for the various arrow types:
  192.  */
  193.  
  194. static Tk_Uid noneUid = NULL;
  195. static Tk_Uid firstUid = NULL;
  196. static Tk_Uid lastUid = NULL;
  197. static Tk_Uid bothUid = NULL;
  198.  
  199. /*
  200.  * The definition below determines how large are static arrays
  201.  * used to hold spline points (splines larger than this have to
  202.  * have their arrays malloc-ed).
  203.  */
  204.  
  205. #define MAX_STATIC_POINTS 200
  206.  
  207. /*
  208.  *--------------------------------------------------------------
  209.  *
  210.  * CreateLine --
  211.  *
  212.  *    This procedure is invoked to create a new line item in
  213.  *    a canvas.
  214.  *
  215.  * Results:
  216.  *    A standard Tcl return value.  If an error occurred in
  217.  *    creating the item, then an error message is left in
  218.  *    interp->result;  in this case itemPtr is left uninitialized,
  219.  *    so it can be safely freed by the caller.
  220.  *
  221.  * Side effects:
  222.  *    A new line item is created.
  223.  *
  224.  *--------------------------------------------------------------
  225.  */
  226.  
  227. static int
  228. CreateLine(interp, canvas, itemPtr, argc, argv)
  229.     Tcl_Interp *interp;            /* Interpreter for error reporting. */
  230.     Tk_Canvas canvas;            /* Canvas to hold new item. */
  231.     Tk_Item *itemPtr;            /* Record to hold new item;  header
  232.                      * has been initialized by caller. */
  233.     int argc;                /* Number of arguments in argv. */
  234.     char **argv;            /* Arguments describing line. */
  235. {
  236.     LineItem *linePtr = (LineItem *) itemPtr;
  237.     int i;
  238.  
  239.     if (argc < 4) {
  240.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  241.         Tk_PathName(Tk_CanvasTkwin(canvas)), " create ",
  242.         itemPtr->typePtr->name, " x1 y1 x2 y2 ?x3 y3 ...? ?options?\"",
  243.         (char *) NULL);
  244.     return TCL_ERROR;
  245.     }
  246.  
  247.     /*
  248.      * Carry out initialization that is needed to set defaults and to
  249.      * allow proper cleanup after errors during the the remainder of
  250.      * this procedure.
  251.      */
  252.  
  253.     linePtr->canvas = canvas;
  254.     linePtr->numPoints = 0;
  255.     linePtr->coordPtr = NULL;
  256.     linePtr->width = 1;
  257.     linePtr->fg = None;
  258.     linePtr->fillStipple = None;
  259.     linePtr->capStyle = CapButt;
  260.     linePtr->joinStyle = JoinRound;
  261.     linePtr->gc = None;
  262.     linePtr->arrowGC = None;
  263.     if (noneUid == NULL) {
  264.     noneUid = Tk_GetUid("none");
  265.     firstUid = Tk_GetUid("first");
  266.     lastUid = Tk_GetUid("last");
  267.     bothUid = Tk_GetUid("both");
  268.     }
  269.     linePtr->arrow = noneUid;
  270.     linePtr->arrowShapeA = 8.0;
  271.     linePtr->arrowShapeB = 10.0;
  272.     linePtr->arrowShapeC = 3.0;
  273.     linePtr->firstArrowPtr = NULL;
  274.     linePtr->lastArrowPtr = NULL;
  275.     linePtr->smooth = 0;
  276.     linePtr->splineSteps = 12;
  277.  
  278.     /*
  279.      * Count the number of points and then parse them into a point
  280.      * array.  Leading arguments are assumed to be points if they
  281.      * start with a digit or a minus sign followed by a digit.
  282.      */
  283.  
  284.     for (i = 4; i < (argc-1); i+=2) {
  285.     if ((!isdigit(UCHAR(argv[i][0]))) &&
  286.         ((argv[i][0] != '-')
  287.         || ((argv[i][1] != '.') && !isdigit(UCHAR(argv[i][1]))))) {
  288.         break;
  289.     }
  290.     }
  291.     if (LineCoords(interp, canvas, itemPtr, i, argv) != TCL_OK) {
  292.     goto error;
  293.     }
  294.     if (ConfigureLine(interp, canvas, itemPtr, argc-i, argv+i, 0) == TCL_OK) {
  295.     return TCL_OK;
  296.     }
  297.  
  298.     error:
  299.     DeleteLine(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
  300.     return TCL_ERROR;
  301. }
  302.  
  303. /*
  304.  *--------------------------------------------------------------
  305.  *
  306.  * LineCoords --
  307.  *
  308.  *    This procedure is invoked to process the "coords" widget
  309.  *    command on lines.  See the user documentation for details
  310.  *    on what it does.
  311.  *
  312.  * Results:
  313.  *    Returns TCL_OK or TCL_ERROR, and sets interp->result.
  314.  *
  315.  * Side effects:
  316.  *    The coordinates for the given item may be changed.
  317.  *
  318.  *--------------------------------------------------------------
  319.  */
  320.  
  321. static int
  322. LineCoords(interp, canvas, itemPtr, argc, argv)
  323.     Tcl_Interp *interp;            /* Used for error reporting. */
  324.     Tk_Canvas canvas;            /* Canvas containing item. */
  325.     Tk_Item *itemPtr;            /* Item whose coordinates are to be
  326.                      * read or modified. */
  327.     int argc;                /* Number of coordinates supplied in
  328.                      * argv. */
  329.     char **argv;            /* Array of coordinates: x1, y1,
  330.                      * x2, y2, ... */
  331. {
  332.     LineItem *linePtr = (LineItem *) itemPtr;
  333.     char buffer[TCL_DOUBLE_SPACE];
  334.     int i, numPoints;
  335.  
  336.     if (argc == 0) {
  337.     double *coordPtr;
  338.     int numCoords;
  339.  
  340.     numCoords = 2*linePtr->numPoints;
  341.     if (linePtr->firstArrowPtr != NULL) {
  342.         coordPtr = linePtr->firstArrowPtr;
  343.     } else {
  344.         coordPtr = linePtr->coordPtr;
  345.     }
  346.     for (i = 0; i < numCoords; i++, coordPtr++) {
  347.         if (i == 2) {
  348.         coordPtr = linePtr->coordPtr+2;
  349.         }
  350.         if ((linePtr->lastArrowPtr != NULL) && (i == (numCoords-2))) {
  351.         coordPtr = linePtr->lastArrowPtr;
  352.         }
  353.         Tcl_PrintDouble(interp, *coordPtr, buffer);
  354.         Tcl_AppendElement(interp, buffer);
  355.     }
  356.     } else if (argc < 4) {
  357.     Tcl_AppendResult(interp,
  358.         "too few coordinates for line: must have at least 4",
  359.         (char *) NULL);
  360.     return TCL_ERROR;
  361.     } else if (argc & 1) {
  362.     Tcl_AppendResult(interp,
  363.         "odd number of coordinates specified for line",
  364.         (char *) NULL);
  365.     return TCL_ERROR;
  366.     } else {
  367.     numPoints = argc/2;
  368.     if (linePtr->numPoints != numPoints) {
  369.         if (linePtr->coordPtr != NULL) {
  370.         ckfree((char *) linePtr->coordPtr);
  371.         }
  372.         linePtr->coordPtr = (double *) ckalloc((unsigned)
  373.             (sizeof(double) * argc));
  374.         linePtr->numPoints = numPoints;
  375.     }
  376.     for (i = argc-1; i >= 0; i--) {
  377.         if (Tk_CanvasGetCoord(interp, canvas, argv[i],
  378.             &linePtr->coordPtr[i]) != TCL_OK) {
  379.         return TCL_ERROR;
  380.         }
  381.     }
  382.  
  383.     /*
  384.      * Update arrowheads by throwing away any existing arrow-head
  385.      * information and calling ConfigureArrows to recompute it.
  386.      */
  387.  
  388.     if (linePtr->firstArrowPtr != NULL) {
  389.         ckfree((char *) linePtr->firstArrowPtr);
  390.         linePtr->firstArrowPtr = NULL;
  391.     }
  392.     if (linePtr->lastArrowPtr != NULL) {
  393.         ckfree((char *) linePtr->lastArrowPtr);
  394.         linePtr->lastArrowPtr = NULL;
  395.     }
  396.     if (linePtr->arrow != noneUid) {
  397.         ConfigureArrows(canvas, linePtr);
  398.     }
  399.     ComputeLineBbox(canvas, linePtr);
  400.     }
  401.     return TCL_OK;
  402. }
  403.  
  404. /*
  405.  *--------------------------------------------------------------
  406.  *
  407.  * ConfigureLine --
  408.  *
  409.  *    This procedure is invoked to configure various aspects
  410.  *    of a line item such as its background color.
  411.  *
  412.  * Results:
  413.  *    A standard Tcl result code.  If an error occurs, then
  414.  *    an error message is left in interp->result.
  415.  *
  416.  * Side effects:
  417.  *    Configuration information, such as colors and stipple
  418.  *    patterns, may be set for itemPtr.
  419.  *
  420.  *--------------------------------------------------------------
  421.  */
  422.  
  423. static int
  424. ConfigureLine(interp, canvas, itemPtr, argc, argv, flags)
  425.     Tcl_Interp *interp;        /* Used for error reporting. */
  426.     Tk_Canvas canvas;        /* Canvas containing itemPtr. */
  427.     Tk_Item *itemPtr;        /* Line item to reconfigure. */
  428.     int argc;            /* Number of elements in argv.  */
  429.     char **argv;        /* Arguments describing things to configure. */
  430.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  431. {
  432.     LineItem *linePtr = (LineItem *) itemPtr;
  433.     XGCValues gcValues;
  434.     GC newGC, arrowGC;
  435.     unsigned long mask;
  436.     Tk_Window tkwin;
  437.  
  438.     tkwin = Tk_CanvasTkwin(canvas);
  439.     if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc, argv,
  440.         (char *) linePtr, flags) != TCL_OK) {
  441.     return TCL_ERROR;
  442.     }
  443.  
  444.     /*
  445.      * A few of the options require additional processing, such as
  446.      * graphics contexts.
  447.      */
  448.  
  449.     if (linePtr->fg == NULL) {
  450.     newGC = arrowGC = None;
  451.     } else {
  452.     gcValues.foreground = linePtr->fg->pixel;
  453.     gcValues.join_style = linePtr->joinStyle;
  454.     if (linePtr->width < 0) {
  455.         linePtr->width = 1;
  456.     }
  457.     gcValues.line_width = linePtr->width;
  458.     mask = GCForeground|GCJoinStyle|GCLineWidth;
  459.     if (linePtr->fillStipple != None) {
  460.         gcValues.stipple = linePtr->fillStipple;
  461.         gcValues.fill_style = FillStippled;
  462.         mask |= GCStipple|GCFillStyle;
  463.     }
  464.     if (linePtr->arrow == noneUid) {
  465.         gcValues.cap_style = linePtr->capStyle;
  466.         mask |= GCCapStyle;
  467.     }
  468.     newGC = Tk_GetGC(tkwin, mask, &gcValues);
  469.     gcValues.line_width = 0;
  470.     arrowGC = Tk_GetGC(tkwin, mask, &gcValues);
  471.     }
  472.     if (linePtr->gc != None) {
  473.     Tk_FreeGC(Tk_Display(tkwin), linePtr->gc);
  474.     }
  475.     if (linePtr->arrowGC != None) {
  476.     Tk_FreeGC(Tk_Display(tkwin), linePtr->arrowGC);
  477.     }
  478.     linePtr->gc = newGC;
  479.     linePtr->arrowGC = arrowGC;
  480.  
  481.     /*
  482.      * Keep spline parameters within reasonable limits.
  483.      */
  484.  
  485.     if (linePtr->splineSteps < 1) {
  486.     linePtr->splineSteps = 1;
  487.     } else if (linePtr->splineSteps > 100) {
  488.     linePtr->splineSteps = 100;
  489.     }
  490.  
  491.     /*
  492.      * Setup arrowheads, if needed.  If arrowheads are turned off,
  493.      * restore the line's endpoints (they were shortened when the
  494.      * arrowheads were added).
  495.      */
  496.  
  497.     if ((linePtr->firstArrowPtr != NULL) && (linePtr->arrow != firstUid)
  498.         && (linePtr->arrow != bothUid)) {
  499.     linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
  500.     linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
  501.     ckfree((char *) linePtr->firstArrowPtr);
  502.     linePtr->firstArrowPtr = NULL;
  503.     }
  504.     if ((linePtr->lastArrowPtr != NULL) && (linePtr->arrow != lastUid)
  505.         && (linePtr->arrow != bothUid)) {
  506.     int i;
  507.  
  508.     i = 2*(linePtr->numPoints-1);
  509.     linePtr->coordPtr[i] = linePtr->lastArrowPtr[0];
  510.     linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1];
  511.     ckfree((char *) linePtr->lastArrowPtr);
  512.     linePtr->lastArrowPtr = NULL;
  513.     }
  514.     if (linePtr->arrow != noneUid) {
  515.     if ((linePtr->arrow != firstUid) && (linePtr->arrow != lastUid)
  516.         && (linePtr->arrow != bothUid)) {
  517.         Tcl_AppendResult(interp, "bad arrow spec \"",
  518.             linePtr->arrow, "\": must be none, first, last, or both",
  519.             (char *) NULL);
  520.         linePtr->arrow = noneUid;
  521.         return TCL_ERROR;
  522.     }
  523.     ConfigureArrows(canvas, linePtr);
  524.     }
  525.  
  526.     /*
  527.      * Recompute bounding box for line.
  528.      */
  529.  
  530.     ComputeLineBbox(canvas, linePtr);
  531.  
  532.     return TCL_OK;
  533. }
  534.  
  535. /*
  536.  *--------------------------------------------------------------
  537.  *
  538.  * DeleteLine --
  539.  *
  540.  *    This procedure is called to clean up the data structure
  541.  *    associated with a line item.
  542.  *
  543.  * Results:
  544.  *    None.
  545.  *
  546.  * Side effects:
  547.  *    Resources associated with itemPtr are released.
  548.  *
  549.  *--------------------------------------------------------------
  550.  */
  551.  
  552. static void
  553. DeleteLine(canvas, itemPtr, display)
  554.     Tk_Canvas canvas;            /* Info about overall canvas widget. */
  555.     Tk_Item *itemPtr;            /* Item that is being deleted. */
  556.     Display *display;            /* Display containing window for
  557.                      * canvas. */
  558. {
  559.     LineItem *linePtr = (LineItem *) itemPtr;
  560.  
  561.     if (linePtr->coordPtr != NULL) {
  562.     ckfree((char *) linePtr->coordPtr);
  563.     }
  564.     if (linePtr->fg != NULL) {
  565.     Tk_FreeColor(linePtr->fg);
  566.     }
  567.     if (linePtr->fillStipple != None) {
  568.     Tk_FreeBitmap(display, linePtr->fillStipple);
  569.     }
  570.     if (linePtr->gc != None) {
  571.     Tk_FreeGC(display, linePtr->gc);
  572.     }
  573.     if (linePtr->arrowGC != None) {
  574.     Tk_FreeGC(display, linePtr->arrowGC);
  575.     }
  576.     if (linePtr->firstArrowPtr != NULL) {
  577.     ckfree((char *) linePtr->firstArrowPtr);
  578.     }
  579.     if (linePtr->lastArrowPtr != NULL) {
  580.     ckfree((char *) linePtr->lastArrowPtr);
  581.     }
  582. }
  583.  
  584. /*
  585.  *--------------------------------------------------------------
  586.  *
  587.  * ComputeLineBbox --
  588.  *
  589.  *    This procedure is invoked to compute the bounding box of
  590.  *    all the pixels that may be drawn as part of a line.
  591.  *
  592.  * Results:
  593.  *    None.
  594.  *
  595.  * Side effects:
  596.  *    The fields x1, y1, x2, and y2 are updated in the header
  597.  *    for itemPtr.
  598.  *
  599.  *--------------------------------------------------------------
  600.  */
  601.  
  602. static void
  603. ComputeLineBbox(canvas, linePtr)
  604.     Tk_Canvas canvas;            /* Canvas that contains item. */
  605.     LineItem *linePtr;            /* Item whose bbos is to be
  606.                      * recomputed. */
  607. {
  608.     double *coordPtr;
  609.     int i;
  610.  
  611.     coordPtr = linePtr->coordPtr;
  612.     linePtr->header.x1 = linePtr->header.x2 = *coordPtr;
  613.     linePtr->header.y1 = linePtr->header.y2 = coordPtr[1];
  614.  
  615.     /*
  616.      * Compute the bounding box of all the points in the line,
  617.      * then expand in all directions by the line's width to take
  618.      * care of butting or rounded corners and projecting or
  619.      * rounded caps.  This expansion is an overestimate (worst-case
  620.      * is square root of two over two) but it's simple.  Don't do
  621.      * anything special for curves.  This causes an additional
  622.      * overestimate in the bounding box, but is faster.
  623.      */
  624.  
  625.     for (i = 1, coordPtr = linePtr->coordPtr+2; i < linePtr->numPoints;
  626.         i++, coordPtr += 2) {
  627.     TkIncludePoint((Tk_Item *) linePtr, coordPtr);
  628.     }
  629.     linePtr->header.x1 -= linePtr->width;
  630.     linePtr->header.x2 += linePtr->width;
  631.     linePtr->header.y1 -= linePtr->width;
  632.     linePtr->header.y2 += linePtr->width;
  633.  
  634.     /*
  635.      * For mitered lines, make a second pass through all the points.
  636.      * Compute the locations of the two miter vertex points and add
  637.      * those into the bounding box.
  638.      */
  639.  
  640.     if (linePtr->joinStyle == JoinMiter) {
  641.     for (i = linePtr->numPoints, coordPtr = linePtr->coordPtr; i >= 3;
  642.         i--, coordPtr += 2) {
  643.         double miter[4];
  644.         int j;
  645.     
  646.         if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
  647.             (double) linePtr->width, miter, miter+2)) {
  648.         for (j = 0; j < 4; j += 2) {
  649.             TkIncludePoint((Tk_Item *) linePtr, miter+j);
  650.         }
  651.         }
  652.     }
  653.     }
  654.  
  655.     /*
  656.      * Add in the sizes of arrowheads, if any.
  657.      */
  658.  
  659.     if (linePtr->arrow != noneUid) {
  660.     if (linePtr->arrow != lastUid) {
  661.         for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
  662.             i++, coordPtr += 2) {
  663.         TkIncludePoint((Tk_Item *) linePtr, coordPtr);
  664.         }
  665.     }
  666.     if (linePtr->arrow != firstUid) {
  667.         for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
  668.             i++, coordPtr += 2) {
  669.         TkIncludePoint((Tk_Item *) linePtr, coordPtr);
  670.         }
  671.     }
  672.     }
  673.  
  674.     /*
  675.      * Add one more pixel of fudge factor just to be safe (e.g.
  676.      * X may round differently than we do).
  677.      */
  678.  
  679.     linePtr->header.x1 -= 1;
  680.     linePtr->header.x2 += 1;
  681.     linePtr->header.y1 -= 1;
  682.     linePtr->header.y2 += 1;
  683. }
  684.  
  685. /*
  686.  *--------------------------------------------------------------
  687.  *
  688.  * DisplayLine --
  689.  *
  690.  *    This procedure is invoked to draw a line item in a given
  691.  *    drawable.
  692.  *
  693.  * Results:
  694.  *    None.
  695.  *
  696.  * Side effects:
  697.  *    ItemPtr is drawn in drawable using the transformation
  698.  *    information in canvas.
  699.  *
  700.  *--------------------------------------------------------------
  701.  */
  702.  
  703. static void
  704. DisplayLine(canvas, itemPtr, display, drawable, x, y, width, height)
  705.     Tk_Canvas canvas;            /* Canvas that contains item. */
  706.     Tk_Item *itemPtr;            /* Item to be displayed. */
  707.     Display *display;            /* Display on which to draw item. */
  708.     Drawable drawable;            /* Pixmap or window in which to draw
  709.                      * item. */
  710.     int x, y, width, height;        /* Describes region of canvas that
  711.                      * must be redisplayed (not used). */
  712. {
  713.     LineItem *linePtr = (LineItem *) itemPtr;
  714.     XPoint staticPoints[MAX_STATIC_POINTS];
  715.     XPoint *pointPtr;
  716.     XPoint *pPtr;
  717.     double *coordPtr;
  718.     int i, numPoints;
  719.  
  720.     if (linePtr->gc == None) {
  721.     return;
  722.     }
  723.  
  724.     /*
  725.      * Build up an array of points in screen coordinates.  Use a
  726.      * static array unless the line has an enormous number of points;
  727.      * in this case, dynamically allocate an array.  For smoothed lines,
  728.      * generate the curve points on each redisplay.
  729.      */
  730.  
  731.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  732.     numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  733.     } else {
  734.     numPoints = linePtr->numPoints;
  735.     }
  736.  
  737.     if (numPoints <= MAX_STATIC_POINTS) {
  738.     pointPtr = staticPoints;
  739.     } else {
  740.     pointPtr = (XPoint *) ckalloc((unsigned) (numPoints * sizeof(XPoint)));
  741.     }
  742.  
  743.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  744.     numPoints = TkMakeBezierCurve(canvas, linePtr->coordPtr,
  745.         linePtr->numPoints, linePtr->splineSteps, pointPtr,
  746.         (double *) NULL);
  747.     } else {
  748.     for (i = 0, coordPtr = linePtr->coordPtr, pPtr = pointPtr;
  749.         i < linePtr->numPoints;  i += 1, coordPtr += 2, pPtr++) {
  750.         Tk_CanvasDrawableCoords(canvas, coordPtr[0], coordPtr[1],
  751.             &pPtr->x, &pPtr->y);
  752.     }
  753.     }
  754.  
  755.     /*
  756.      * Display line, the free up line storage if it was dynamically
  757.      * allocated.  If we're stippling, then modify the stipple offset
  758.      * in the GC.  Be sure to reset the offset when done, since the
  759.      * GC is supposed to be read-only.
  760.      */
  761.  
  762.     if (linePtr->fillStipple != None) {
  763.     Tk_CanvasSetStippleOrigin(canvas, linePtr->gc);
  764.     Tk_CanvasSetStippleOrigin(canvas, linePtr->arrowGC);
  765.     }
  766.     XDrawLines(display, drawable, linePtr->gc, pointPtr, numPoints,
  767.         CoordModeOrigin);
  768.     if (pointPtr != staticPoints) {
  769.     ckfree((char *) pointPtr);
  770.     }
  771.  
  772.     /*
  773.      * Display arrowheads, if they are wanted.
  774.      */
  775.  
  776.     if (linePtr->firstArrowPtr != NULL) {
  777.     TkFillPolygon(canvas, linePtr->firstArrowPtr, PTS_IN_ARROW,
  778.         display, drawable, linePtr->gc, NULL);
  779.     }
  780.     if (linePtr->lastArrowPtr != NULL) {
  781.     TkFillPolygon(canvas, linePtr->lastArrowPtr, PTS_IN_ARROW,
  782.         display, drawable, linePtr->gc, NULL);
  783.     }
  784.     if (linePtr->fillStipple != None) {
  785.     XSetTSOrigin(display, linePtr->gc, 0, 0);
  786.     XSetTSOrigin(display, linePtr->arrowGC, 0, 0);
  787.     }
  788. }
  789.  
  790. /*
  791.  *--------------------------------------------------------------
  792.  *
  793.  * LineToPoint --
  794.  *
  795.  *    Computes the distance from a given point to a given
  796.  *    line, in canvas units.
  797.  *
  798.  * Results:
  799.  *    The return value is 0 if the point whose x and y coordinates
  800.  *    are pointPtr[0] and pointPtr[1] is inside the line.  If the
  801.  *    point isn't inside the line then the return value is the
  802.  *    distance from the point to the line.
  803.  *
  804.  * Side effects:
  805.  *    None.
  806.  *
  807.  *--------------------------------------------------------------
  808.  */
  809.  
  810.     /* ARGSUSED */
  811. static double
  812. LineToPoint(canvas, itemPtr, pointPtr)
  813.     Tk_Canvas canvas;        /* Canvas containing item. */
  814.     Tk_Item *itemPtr;        /* Item to check against point. */
  815.     double *pointPtr;        /* Pointer to x and y coordinates. */
  816. {
  817.     LineItem *linePtr = (LineItem *) itemPtr;
  818.     double *coordPtr, *linePoints;
  819.     double staticSpace[2*MAX_STATIC_POINTS];
  820.     double poly[10];
  821.     double bestDist, dist;
  822.     int numPoints, count;
  823.     int changedMiterToBevel;    /* Non-zero means that a mitered corner
  824.                  * had to be treated as beveled after all
  825.                  * because the angle was < 11 degrees. */
  826.  
  827.     bestDist = 1.0e36;
  828.  
  829.     /*
  830.      * Handle smoothed lines by generating an expanded set of points
  831.      * against which to do the check.
  832.      */
  833.  
  834.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  835.     numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  836.     if (numPoints <= MAX_STATIC_POINTS) {
  837.         linePoints = staticSpace;
  838.     } else {
  839.         linePoints = (double *) ckalloc((unsigned)
  840.             (2*numPoints*sizeof(double)));
  841.     }
  842.     numPoints = TkMakeBezierCurve(canvas, linePtr->coordPtr,
  843.         linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
  844.         linePoints);
  845.     } else {
  846.     numPoints = linePtr->numPoints;
  847.     linePoints = linePtr->coordPtr;
  848.     }
  849.  
  850.     /*
  851.      * The overall idea is to iterate through all of the edges of
  852.      * the line, computing a polygon for each edge and testing the
  853.      * point against that polygon.  In addition, there are additional
  854.      * tests to deal with rounded joints and caps.
  855.      */
  856.  
  857.     changedMiterToBevel = 0;
  858.     for (count = numPoints, coordPtr = linePoints; count >= 2;
  859.         count--, coordPtr += 2) {
  860.  
  861.     /*
  862.      * If rounding is done around the first point then compute
  863.      * the distance between the point and the point.
  864.      */
  865.  
  866.     if (((linePtr->capStyle == CapRound) && (count == numPoints))
  867.         || ((linePtr->joinStyle == JoinRound)
  868.             && (count != numPoints))) {
  869.         dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
  870.             - linePtr->width/2.0;
  871.         if (dist <= 0.0) {
  872.         bestDist = 0.0;
  873.         goto done;
  874.         } else if (dist < bestDist) {
  875.         bestDist = dist;
  876.         }
  877.     }
  878.  
  879.     /*
  880.      * Compute the polygonal shape corresponding to this edge,
  881.      * consisting of two points for the first point of the edge
  882.      * and two points for the last point of the edge.
  883.      */
  884.  
  885.     if (count == numPoints) {
  886.         TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width,
  887.             linePtr->capStyle == CapProjecting, poly, poly+2);
  888.     } else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) {
  889.         poly[0] = poly[6];
  890.         poly[1] = poly[7];
  891.         poly[2] = poly[4];
  892.         poly[3] = poly[5];
  893.     } else {
  894.         TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width, 0,
  895.             poly, poly+2);
  896.  
  897.         /*
  898.          * If this line uses beveled joints, then check the distance
  899.          * to a polygon comprising the last two points of the previous
  900.          * polygon and the first two from this polygon;  this checks
  901.          * the wedges that fill the mitered joint.
  902.          */
  903.  
  904.         if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) {
  905.         poly[8] = poly[0];
  906.         poly[9] = poly[1];
  907.         dist = TkPolygonToPoint(poly, 5, pointPtr);
  908.         if (dist <= 0.0) {
  909.             bestDist = 0.0;
  910.             goto done;
  911.         } else if (dist < bestDist) {
  912.             bestDist = dist;
  913.         }
  914.         changedMiterToBevel = 0;
  915.         }
  916.     }
  917.     if (count == 2) {
  918.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width,
  919.             linePtr->capStyle == CapProjecting, poly+4, poly+6);
  920.     } else if (linePtr->joinStyle == JoinMiter) {
  921.         if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
  922.             (double) linePtr->width, poly+4, poly+6) == 0) {
  923.         changedMiterToBevel = 1;
  924.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width,
  925.             0, poly+4, poly+6);
  926.         }
  927.     } else {
  928.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width, 0,
  929.             poly+4, poly+6);
  930.     }
  931.     poly[8] = poly[0];
  932.     poly[9] = poly[1];
  933.     dist = TkPolygonToPoint(poly, 5, pointPtr);
  934.     if (dist <= 0.0) {
  935.         bestDist = 0.0;
  936.         goto done;
  937.     } else if (dist < bestDist) {
  938.         bestDist = dist;
  939.     }
  940.     }
  941.  
  942.     /*
  943.      * If caps are rounded, check the distance to the cap around the
  944.      * final end point of the line.
  945.      */
  946.  
  947.     if (linePtr->capStyle == CapRound) {
  948.     dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
  949.         - linePtr->width/2.0;
  950.     if (dist <= 0.0) {
  951.         bestDist = 0.0;
  952.         goto done;
  953.     } else if (dist < bestDist) {
  954.         bestDist = dist;
  955.     }
  956.     }
  957.  
  958.     /*
  959.      * If there are arrowheads, check the distance to the arrowheads.
  960.      */
  961.  
  962.     if (linePtr->arrow != noneUid) {
  963.     if (linePtr->arrow != lastUid) {
  964.         dist = TkPolygonToPoint(linePtr->firstArrowPtr, PTS_IN_ARROW,
  965.             pointPtr);
  966.         if (dist <= 0.0) {
  967.         bestDist = 0.0;
  968.         goto done;
  969.         } else if (dist < bestDist) {
  970.         bestDist = dist;
  971.         }
  972.     }
  973.     if (linePtr->arrow != firstUid) {
  974.         dist = TkPolygonToPoint(linePtr->lastArrowPtr, PTS_IN_ARROW,
  975.             pointPtr);
  976.         if (dist <= 0.0) {
  977.         bestDist = 0.0;
  978.         goto done;
  979.         } else if (dist < bestDist) {
  980.         bestDist = dist;
  981.         }
  982.     }
  983.     }
  984.  
  985.     done:
  986.     if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {
  987.     ckfree((char *) linePoints);
  988.     }
  989.     return bestDist;
  990. }
  991.  
  992. /*
  993.  *--------------------------------------------------------------
  994.  *
  995.  * LineToArea --
  996.  *
  997.  *    This procedure is called to determine whether an item
  998.  *    lies entirely inside, entirely outside, or overlapping
  999.  *    a given rectangular area.
  1000.  *
  1001.  * Results:
  1002.  *    -1 is returned if the item is entirely outside the
  1003.  *    area, 0 if it overlaps, and 1 if it is entirely
  1004.  *    inside the given area.
  1005.  *
  1006.  * Side effects:
  1007.  *    None.
  1008.  *
  1009.  *--------------------------------------------------------------
  1010.  */
  1011.  
  1012.     /* ARGSUSED */
  1013. static int
  1014. LineToArea(canvas, itemPtr, rectPtr)
  1015.     Tk_Canvas canvas;        /* Canvas containing item. */
  1016.     Tk_Item *itemPtr;        /* Item to check against line. */
  1017.     double *rectPtr;
  1018. {
  1019.     LineItem *linePtr = (LineItem *) itemPtr;
  1020.     double staticSpace[2*MAX_STATIC_POINTS];
  1021.     double *linePoints;
  1022.     double radius;
  1023.     int numPoints, result;
  1024.  
  1025.     radius = linePtr->width/2.0;
  1026.  
  1027.     /*
  1028.      * Handle smoothed lines by generating an expanded set of points
  1029.      * against which to do the check.
  1030.      */
  1031.  
  1032.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  1033.     numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  1034.     if (numPoints <= MAX_STATIC_POINTS) {
  1035.         linePoints = staticSpace;
  1036.     } else {
  1037.         linePoints = (double *) ckalloc((unsigned)
  1038.             (2*numPoints*sizeof(double)));
  1039.     }
  1040.     numPoints = TkMakeBezierCurve(canvas, linePtr->coordPtr,
  1041.         linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
  1042.         linePoints);
  1043.     } else {
  1044.     numPoints = linePtr->numPoints;
  1045.     linePoints = linePtr->coordPtr;
  1046.     }
  1047.  
  1048.     /*
  1049.      * Check the segments of the line.
  1050.      */
  1051.  
  1052.     result = TkThickPolyLineToArea(linePoints, numPoints, 
  1053.         (double) linePtr->width, linePtr->capStyle, linePtr->joinStyle,
  1054.         rectPtr);
  1055.     if (result == 0) {
  1056.     goto done;
  1057.     }
  1058.  
  1059.     /*
  1060.      * Check arrowheads, if any.
  1061.      */
  1062.  
  1063.     if (linePtr->arrow != noneUid) {
  1064.     if (linePtr->arrow != lastUid) {
  1065.         if (TkPolygonToArea(linePtr->firstArrowPtr, PTS_IN_ARROW,
  1066.             rectPtr) != result) {
  1067.         result = 0;
  1068.         goto done;
  1069.         }
  1070.     }
  1071.     if (linePtr->arrow != firstUid) {
  1072.         if (TkPolygonToArea(linePtr->lastArrowPtr, PTS_IN_ARROW,
  1073.             rectPtr) != result) {
  1074.         result = 0;
  1075.         goto done;
  1076.         }
  1077.     }
  1078.     }
  1079.  
  1080.     done:
  1081.     if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {
  1082.     ckfree((char *) linePoints);
  1083.     }
  1084.     return result;
  1085. }
  1086.  
  1087. /*
  1088.  *--------------------------------------------------------------
  1089.  *
  1090.  * ScaleLine --
  1091.  *
  1092.  *    This procedure is invoked to rescale a line item.
  1093.  *
  1094.  * Results:
  1095.  *    None.
  1096.  *
  1097.  * Side effects:
  1098.  *    The line referred to by itemPtr is rescaled so that the
  1099.  *    following transformation is applied to all point
  1100.  *    coordinates:
  1101.  *        x' = originX + scaleX*(x-originX)
  1102.  *        y' = originY + scaleY*(y-originY)
  1103.  *
  1104.  *--------------------------------------------------------------
  1105.  */
  1106.  
  1107. static void
  1108. ScaleLine(canvas, itemPtr, originX, originY, scaleX, scaleY)
  1109.     Tk_Canvas canvas;            /* Canvas containing line. */
  1110.     Tk_Item *itemPtr;            /* Line to be scaled. */
  1111.     double originX, originY;        /* Origin about which to scale rect. */
  1112.     double scaleX;            /* Amount to scale in X direction. */
  1113.     double scaleY;            /* Amount to scale in Y direction. */
  1114. {
  1115.     LineItem *linePtr = (LineItem *) itemPtr;
  1116.     double *coordPtr;
  1117.     int i;
  1118.  
  1119.     /*
  1120.      * Delete any arrowheads before scaling all the points (so that
  1121.      * the end-points of the line get restored).
  1122.      */
  1123.  
  1124.     if (linePtr->firstArrowPtr != NULL) {
  1125.     linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
  1126.     linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
  1127.     ckfree((char *) linePtr->firstArrowPtr);
  1128.     linePtr->firstArrowPtr = NULL;
  1129.     }
  1130.     if (linePtr->lastArrowPtr != NULL) {
  1131.     int i;
  1132.  
  1133.     i = 2*(linePtr->numPoints-1);
  1134.     linePtr->coordPtr[i] = linePtr->lastArrowPtr[0];
  1135.     linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1];
  1136.     ckfree((char *) linePtr->lastArrowPtr);
  1137.     linePtr->lastArrowPtr = NULL;
  1138.     }
  1139.     for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;
  1140.         i++, coordPtr += 2) {
  1141.     coordPtr[0] = originX + scaleX*(*coordPtr - originX);
  1142.     coordPtr[1] = originY + scaleY*(coordPtr[1] - originY);
  1143.     }
  1144.     if (linePtr->arrow != noneUid) {
  1145.     ConfigureArrows(canvas, linePtr);
  1146.     }
  1147.     ComputeLineBbox(canvas, linePtr);
  1148. }
  1149.  
  1150. /*
  1151.  *--------------------------------------------------------------
  1152.  *
  1153.  * TranslateLine --
  1154.  *
  1155.  *    This procedure is called to move a line by a given amount.
  1156.  *
  1157.  * Results:
  1158.  *    None.
  1159.  *
  1160.  * Side effects:
  1161.  *    The position of the line is offset by (xDelta, yDelta), and
  1162.  *    the bounding box is updated in the generic part of the item
  1163.  *    structure.
  1164.  *
  1165.  *--------------------------------------------------------------
  1166.  */
  1167.  
  1168. static void
  1169. TranslateLine(canvas, itemPtr, deltaX, deltaY)
  1170.     Tk_Canvas canvas;            /* Canvas containing item. */
  1171.     Tk_Item *itemPtr;            /* Item that is being moved. */
  1172.     double deltaX, deltaY;        /* Amount by which item is to be
  1173.                      * moved. */
  1174. {
  1175.     LineItem *linePtr = (LineItem *) itemPtr;
  1176.     double *coordPtr;
  1177.     int i;
  1178.  
  1179.     for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;
  1180.         i++, coordPtr += 2) {
  1181.     coordPtr[0] += deltaX;
  1182.     coordPtr[1] += deltaY;
  1183.     }
  1184.     if (linePtr->firstArrowPtr != NULL) {
  1185.     for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
  1186.         i++, coordPtr += 2) {
  1187.         coordPtr[0] += deltaX;
  1188.         coordPtr[1] += deltaY;
  1189.     }
  1190.     }
  1191.     if (linePtr->lastArrowPtr != NULL) {
  1192.     for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
  1193.         i++, coordPtr += 2) {
  1194.         coordPtr[0] += deltaX;
  1195.         coordPtr[1] += deltaY;
  1196.     }
  1197.     }
  1198.     ComputeLineBbox(canvas, linePtr);
  1199. }
  1200.  
  1201. /*
  1202.  *--------------------------------------------------------------
  1203.  *
  1204.  * ParseArrowShape --
  1205.  *
  1206.  *    This procedure is called back during option parsing to
  1207.  *    parse arrow shape information.
  1208.  *
  1209.  * Results:
  1210.  *    The return value is a standard Tcl result:  TCL_OK means
  1211.  *    that the arrow shape information was parsed ok, and
  1212.  *    TCL_ERROR means it couldn't be parsed.
  1213.  *
  1214.  * Side effects:
  1215.  *    Arrow information in recordPtr is updated.
  1216.  *
  1217.  *--------------------------------------------------------------
  1218.  */
  1219.  
  1220.     /* ARGSUSED */
  1221. static int
  1222. ParseArrowShape(clientData, interp, tkwin, value, recordPtr, offset)
  1223.     ClientData clientData;    /* Not used. */
  1224.     Tcl_Interp *interp;        /* Used for error reporting. */
  1225.     Tk_Window tkwin;        /* Not used. */
  1226.     char *value;        /* Textual specification of arrow shape. */
  1227.     char *recordPtr;        /* Pointer to item record in which to
  1228.                  * store arrow information. */
  1229.     int offset;            /* Offset of shape information in widget
  1230.                  * record. */
  1231. {
  1232.     LineItem *linePtr = (LineItem *) recordPtr;
  1233.     double a, b, c;
  1234.     int argc;
  1235.     char **argv = NULL;
  1236.  
  1237.     if (offset != Tk_Offset(LineItem, arrowShapeA)) {
  1238.     panic("ParseArrowShape received bogus offset");
  1239.     }
  1240.  
  1241.     if (Tcl_SplitList(interp, value, &argc, &argv) != TCL_OK) {
  1242.     syntaxError:
  1243.     Tcl_ResetResult(interp);
  1244.     Tcl_AppendResult(interp, "bad arrow shape \"", value,
  1245.         "\": must be list with three numbers", (char *) NULL);
  1246.     if (argv != NULL) {
  1247.         ckfree((char *) argv);
  1248.     }
  1249.     return TCL_ERROR;
  1250.     }
  1251.     if (argc != 3) {
  1252.     goto syntaxError;
  1253.     }
  1254.     if ((Tk_CanvasGetCoord(interp, linePtr->canvas, argv[0], &a) != TCL_OK)
  1255.         || (Tk_CanvasGetCoord(interp, linePtr->canvas, argv[1], &b)
  1256.         != TCL_OK)
  1257.         || (Tk_CanvasGetCoord(interp, linePtr->canvas, argv[2], &c)
  1258.         != TCL_OK)) {
  1259.     goto syntaxError;
  1260.     }
  1261.     linePtr->arrowShapeA = a;
  1262.     linePtr->arrowShapeB = b;
  1263.     linePtr->arrowShapeC = c;
  1264.     ckfree((char *) argv);
  1265.     return TCL_OK;
  1266. }
  1267.  
  1268. /*
  1269.  *--------------------------------------------------------------
  1270.  *
  1271.  * PrintArrowShape --
  1272.  *
  1273.  *    This procedure is a callback invoked by the configuration
  1274.  *    code to return a printable value describing an arrow shape.
  1275.  *
  1276.  * Results:
  1277.  *    None.
  1278.  *
  1279.  * Side effects:
  1280.  *    None.
  1281.  *
  1282.  *--------------------------------------------------------------
  1283.  */
  1284.  
  1285.     /* ARGSUSED */
  1286. static char *
  1287. PrintArrowShape(clientData, tkwin, recordPtr, offset, freeProcPtr)
  1288.     ClientData clientData;    /* Not used. */
  1289.     Tk_Window tkwin;        /* Window associated with linePtr's widget. */
  1290.     char *recordPtr;        /* Pointer to item record containing current
  1291.                  * shape information. */
  1292.     int offset;            /* Offset of arrow information in record. */
  1293.     Tcl_FreeProc **freeProcPtr;    /* Store address of procedure to call to
  1294.                  * free string here. */
  1295. {
  1296.     LineItem *linePtr = (LineItem *) recordPtr;
  1297.     char *buffer;
  1298.  
  1299.     buffer = (char *) ckalloc(120);
  1300. #ifdef STk_CODE
  1301.     sprintf(buffer, "(%.5g %.5g %.5g)", linePtr->arrowShapeA,
  1302.         linePtr->arrowShapeB, linePtr->arrowShapeC);
  1303. #else
  1304.     sprintf(buffer, "%.5g %.5g %.5g", linePtr->arrowShapeA,
  1305.         linePtr->arrowShapeB, linePtr->arrowShapeC);
  1306. #endif
  1307.     *freeProcPtr = TCL_DYNAMIC;
  1308.     return buffer;
  1309. }
  1310.  
  1311. /*
  1312.  *--------------------------------------------------------------
  1313.  *
  1314.  * ConfigureArrows --
  1315.  *
  1316.  *    If arrowheads have been requested for a line, this
  1317.  *    procedure makes arrangements for the arrowheads.
  1318.  *
  1319.  * Results:
  1320.  *    Always returns TCL_OK.
  1321.  *
  1322.  * Side effects:
  1323.  *    Information in linePtr is set up for one or two arrowheads.
  1324.  *    the firstArrowPtr and lastArrowPtr polygons are allocated
  1325.  *    and initialized, if need be, and the end points of the line
  1326.  *    are adjusted so that a thick line doesn't stick out past
  1327.  *    the arrowheads.
  1328.  *
  1329.  *--------------------------------------------------------------
  1330.  */
  1331.  
  1332.     /* ARGSUSED */
  1333. static int
  1334. ConfigureArrows(canvas, linePtr)
  1335.     Tk_Canvas canvas;            /* Canvas in which arrows will be
  1336.                      * displayed (interp and tkwin
  1337.                      * fields are needed). */
  1338.     LineItem *linePtr;            /* Item to configure for arrows. */
  1339. {
  1340.     double *poly, *coordPtr;
  1341.     double dx, dy, length, sinTheta, cosTheta, temp;
  1342.     double fracHeight;            /* Line width as fraction of
  1343.                      * arrowhead width. */
  1344.     double backup;            /* Distance to backup end points
  1345.                      * so the line ends in the middle
  1346.                      * of the arrowhead. */
  1347.     double vertX, vertY;        /* Position of arrowhead vertex. */
  1348.     double shapeA, shapeB, shapeC;    /* Adjusted coordinates (see
  1349.                      * explanation below). */
  1350.  
  1351.     /*
  1352.      * The code below makes a tiny increase in the shape parameters
  1353.      * for the line.  This is a bit of a hack, but it seems to result
  1354.      * in displays that more closely approximate the specified parameters.
  1355.      * Without the adjustment, the arrows come out smaller than expected.
  1356.      */
  1357.  
  1358.     shapeA = linePtr->arrowShapeA + 0.001;
  1359.     shapeB = linePtr->arrowShapeB + 0.001;
  1360.     shapeC = linePtr->arrowShapeC + linePtr->width/2.0 + 0.001;
  1361.  
  1362.     /*
  1363.      * If there's an arrowhead on the first point of the line, compute
  1364.      * its polygon and adjust the first point of the line so that the
  1365.      * line doesn't stick out past the leading edge of the arrowhead.
  1366.      */
  1367.  
  1368.     fracHeight = (linePtr->width/2.0)/shapeC;
  1369.     backup = fracHeight*shapeB + shapeA*(1.0 - fracHeight)/2.0;
  1370.     if (linePtr->arrow != lastUid) {
  1371.     poly = linePtr->firstArrowPtr;
  1372.     if (poly == NULL) {
  1373.         poly = (double *) ckalloc((unsigned)
  1374.             (2*PTS_IN_ARROW*sizeof(double)));
  1375.         poly[0] = poly[10] = linePtr->coordPtr[0];
  1376.         poly[1] = poly[11] = linePtr->coordPtr[1];
  1377.         linePtr->firstArrowPtr = poly;
  1378.     }
  1379.     dx = poly[0] - linePtr->coordPtr[2];
  1380.     dy = poly[1] - linePtr->coordPtr[3];
  1381.     length = hypot(dx, dy);
  1382.     if (length == 0) {
  1383.         sinTheta = cosTheta = 0.0;
  1384.     } else {
  1385.         sinTheta = dy/length;
  1386.         cosTheta = dx/length;
  1387.     }
  1388.     vertX = poly[0] - shapeA*cosTheta;
  1389.     vertY = poly[1] - shapeA*sinTheta;
  1390.     temp = shapeC*sinTheta;
  1391.     poly[2] = poly[0] - shapeB*cosTheta + temp;
  1392.     poly[8] = poly[2] - 2*temp;
  1393.     temp = shapeC*cosTheta;
  1394.     poly[3] = poly[1] - shapeB*sinTheta - temp;
  1395.     poly[9] = poly[3] + 2*temp;
  1396.     poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);
  1397.     poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);
  1398.     poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);
  1399.     poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);
  1400.  
  1401.     /*
  1402.      * Polygon done.  Now move the first point towards the second so
  1403.      * that the corners at the end of the line are inside the
  1404.      * arrowhead.
  1405.      */
  1406.  
  1407.     linePtr->coordPtr[0] = poly[0] - backup*cosTheta;
  1408.     linePtr->coordPtr[1] = poly[1] - backup*sinTheta;
  1409.     }
  1410.  
  1411.     /*
  1412.      * Similar arrowhead calculation for the last point of the line.
  1413.      */
  1414.  
  1415.     if (linePtr->arrow != firstUid) {
  1416.     coordPtr = linePtr->coordPtr + 2*(linePtr->numPoints-2);
  1417.     poly = linePtr->lastArrowPtr;
  1418.     if (poly == NULL) {
  1419.         poly = (double *) ckalloc((unsigned)
  1420.             (2*PTS_IN_ARROW*sizeof(double)));
  1421.         poly[0] = poly[10] = coordPtr[2];
  1422.         poly[1] = poly[11] = coordPtr[3];
  1423.         linePtr->lastArrowPtr = poly;
  1424.     }
  1425.     dx = poly[0] - coordPtr[0];
  1426.     dy = poly[1] - coordPtr[1];
  1427.     length = hypot(dx, dy);
  1428.     if (length == 0) {
  1429.         sinTheta = cosTheta = 0.0;
  1430.     } else {
  1431.         sinTheta = dy/length;
  1432.         cosTheta = dx/length;
  1433.     }
  1434.     vertX = poly[0] - shapeA*cosTheta;
  1435.     vertY = poly[1] - shapeA*sinTheta;
  1436.     temp = shapeC*sinTheta;
  1437.     poly[2] = poly[0] - shapeB*cosTheta + temp;
  1438.     poly[8] = poly[2] - 2*temp;
  1439.     temp = shapeC*cosTheta;
  1440.     poly[3] = poly[1] - shapeB*sinTheta - temp;
  1441.     poly[9] = poly[3] + 2*temp;
  1442.     poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);
  1443.     poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);
  1444.     poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);
  1445.     poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);
  1446.     coordPtr[2] = poly[0] - backup*cosTheta;
  1447.     coordPtr[3] = poly[1] - backup*sinTheta;
  1448.     }
  1449.  
  1450.     return TCL_OK;
  1451. }
  1452.  
  1453. /*
  1454.  *--------------------------------------------------------------
  1455.  *
  1456.  * LineToPostscript --
  1457.  *
  1458.  *    This procedure is called to generate Postscript for
  1459.  *    line items.
  1460.  *
  1461.  * Results:
  1462.  *    The return value is a standard Tcl result.  If an error
  1463.  *    occurs in generating Postscript then an error message is
  1464.  *    left in interp->result, replacing whatever used
  1465.  *    to be there.  If no error occurs, then Postscript for the
  1466.  *    item is appended to the result.
  1467.  *
  1468.  * Side effects:
  1469.  *    None.
  1470.  *
  1471.  *--------------------------------------------------------------
  1472.  */
  1473.  
  1474. static int
  1475. LineToPostscript(interp, canvas, itemPtr, prepass)
  1476.     Tcl_Interp *interp;            /* Leave Postscript or error message
  1477.                      * here. */
  1478.     Tk_Canvas canvas;            /* Information about overall canvas. */
  1479.     Tk_Item *itemPtr;            /* Item for which Postscript is
  1480.                      * wanted. */
  1481.     int prepass;            /* 1 means this is a prepass to
  1482.                      * collect font information;  0 means
  1483.                      * final Postscript is being created. */
  1484. {
  1485.     LineItem *linePtr = (LineItem *) itemPtr;
  1486.     char buffer[200];
  1487.     char *style;
  1488.  
  1489.     if (linePtr->fg == NULL) {
  1490.     return TCL_OK;
  1491.     }
  1492.  
  1493.     /*
  1494.      * Generate a path for the line's center-line (do this differently
  1495.      * for straight lines and smoothed lines).
  1496.      */
  1497.  
  1498.     if ((!linePtr->smooth) || (linePtr->numPoints <= 2)) {
  1499.     Tk_CanvasPsPath(interp, canvas, linePtr->coordPtr, linePtr->numPoints);
  1500.     } else {
  1501.     if (linePtr->fillStipple == None) {
  1502.         TkMakeBezierPostscript(interp, canvas, linePtr->coordPtr,
  1503.             linePtr->numPoints);
  1504.     } else {
  1505.         /*
  1506.          * Special hack: Postscript printers don't appear to be able
  1507.          * to turn a path drawn with "curveto"s into a clipping path
  1508.          * without exceeding resource limits, so TkMakeBezierPostscript
  1509.          * won't work for stippled curves.  Instead, generate all of
  1510.          * the intermediate points here and output them into the
  1511.          * Postscript file with "lineto"s instead.
  1512.          */
  1513.  
  1514.         double staticPoints[2*MAX_STATIC_POINTS];
  1515.         double *pointPtr;
  1516.         int numPoints;
  1517.  
  1518.         numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  1519.         pointPtr = staticPoints;
  1520.         if (numPoints > MAX_STATIC_POINTS) {
  1521.         pointPtr = (double *) ckalloc((unsigned)
  1522.             (numPoints * 2 * sizeof(double)));
  1523.         }
  1524.         numPoints = TkMakeBezierCurve(canvas, linePtr->coordPtr,
  1525.             linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
  1526.             pointPtr);
  1527.         Tk_CanvasPsPath(interp, canvas, pointPtr, numPoints);
  1528.         if (pointPtr != staticPoints) {
  1529.         ckfree((char *) pointPtr);
  1530.         }
  1531.     }
  1532.     }
  1533.  
  1534.     /*
  1535.      * Set other line-drawing parameters and stroke out the line.
  1536.      */
  1537.  
  1538.     sprintf(buffer, "%d setlinewidth\n", linePtr->width);
  1539.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  1540.     style = "0 setlinecap\n";
  1541.     if (linePtr->capStyle == CapRound) {
  1542.     style = "1 setlinecap\n";
  1543.     } else if (linePtr->capStyle == CapProjecting) {
  1544.     style = "2 setlinecap\n";
  1545.     }
  1546.     Tcl_AppendResult(interp, style, (char *) NULL);
  1547.     style = "0 setlinejoin\n";
  1548.     if (linePtr->joinStyle == JoinRound) {
  1549.     style = "1 setlinejoin\n";
  1550.     } else if (linePtr->joinStyle == JoinBevel) {
  1551.     style = "2 setlinejoin\n";
  1552.     }
  1553.     Tcl_AppendResult(interp, style, (char *) NULL);
  1554.     if (Tk_CanvasPsColor(interp, canvas, linePtr->fg) != TCL_OK) {
  1555.     return TCL_ERROR;
  1556.     };
  1557.     if (linePtr->fillStipple != None) {
  1558.     Tcl_AppendResult(interp, "StrokeClip ", (char *) NULL);
  1559.     if (Tk_CanvasPsStipple(interp, canvas, linePtr->fillStipple)
  1560.         != TCL_OK) {
  1561.         return TCL_ERROR;
  1562.     }
  1563.     } else {
  1564.     Tcl_AppendResult(interp, "stroke\n", (char *) NULL);
  1565.     }
  1566.  
  1567.     /*
  1568.      * Output polygons for the arrowheads, if there are any.
  1569.      */
  1570.  
  1571.     if (linePtr->firstArrowPtr != NULL) {
  1572.     if (linePtr->fillStipple != None) {
  1573.         Tcl_AppendResult(interp, "grestore gsave\n",
  1574.             (char *) NULL);
  1575.     }
  1576.     if (ArrowheadPostscript(interp, canvas, linePtr,
  1577.         linePtr->firstArrowPtr) != TCL_OK) {
  1578.         return TCL_ERROR;
  1579.     }
  1580.     }
  1581.     if (linePtr->lastArrowPtr != NULL) {
  1582.     if (linePtr->fillStipple != None) {
  1583.         Tcl_AppendResult(interp, "grestore gsave\n", (char *) NULL);
  1584.     }
  1585.     if (ArrowheadPostscript(interp, canvas, linePtr,
  1586.         linePtr->lastArrowPtr) != TCL_OK) {
  1587.         return TCL_ERROR;
  1588.     }
  1589.     }
  1590.     return TCL_OK;
  1591. }
  1592.  
  1593. /*
  1594.  *--------------------------------------------------------------
  1595.  *
  1596.  * ArrowheadPostscript --
  1597.  *
  1598.  *    This procedure is called to generate Postscript for
  1599.  *    an arrowhead for a line item.
  1600.  *
  1601.  * Results:
  1602.  *    The return value is a standard Tcl result.  If an error
  1603.  *    occurs in generating Postscript then an error message is
  1604.  *    left in interp->result, replacing whatever used
  1605.  *    to be there.  If no error occurs, then Postscript for the
  1606.  *    arrowhead is appended to the result.
  1607.  *
  1608.  * Side effects:
  1609.  *    None.
  1610.  *
  1611.  *--------------------------------------------------------------
  1612.  */
  1613.  
  1614. static int
  1615. ArrowheadPostscript(interp, canvas, linePtr, arrowPtr)
  1616.     Tcl_Interp *interp;            /* Leave Postscript or error message
  1617.                      * here. */
  1618.     Tk_Canvas canvas;            /* Information about overall canvas. */
  1619.     LineItem *linePtr;            /* Line item for which Postscript is
  1620.                      * being generated. */
  1621.     double *arrowPtr;            /* Pointer to first of five points
  1622.                      * describing arrowhead polygon. */
  1623. {
  1624.     Tk_CanvasPsPath(interp, canvas, arrowPtr, PTS_IN_ARROW);
  1625.     if (linePtr->fillStipple != None) {
  1626.     Tcl_AppendResult(interp, "clip ", (char *) NULL);
  1627.     if (Tk_CanvasPsStipple(interp, canvas, linePtr->fillStipple)
  1628.         != TCL_OK) {
  1629.         return TCL_ERROR;
  1630.     }
  1631.     } else {
  1632.     Tcl_AppendResult(interp, "fill\n", (char *) NULL);
  1633.     }
  1634.     return TCL_OK;
  1635. }
  1636.